
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: XMLData.java 377 2010-03-11 20:50:18Z archie.cobbs $
 */

package org.dellroad.sidekar.xml;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeMap;

import org.dellroad.sidekar.Entity;
import org.dellroad.sidekar.Item;
import org.dellroad.sidekar.util.MultiIterator;
import org.dellroad.sidekar.util.SimpleIterator;

/**
 * Class associated with the &lt;data&gt; element in the JiBX binding context.
 */
class XMLData {

    private final XMLDatabase xmlDatabase;
    private final String entityName;
    private final Iterator<XMLItem> iterator;

    // Used during both marshalling and unmarshalling
    public XMLData(XMLDatabase xmlDatabase) {
        this(xmlDatabase, null);
    }

    // Used during marshalling
    public XMLData(XMLDatabase xmlDatabase, String entityName) {
        if (xmlDatabase == null)
            throw new IllegalArgumentException("null xmlDatabase");
        this.xmlDatabase = xmlDatabase;
        this.entityName = entityName;
        this.iterator = null;
    }

    // Used during marshalling
    public XMLData(final Iterator<?> iterator) {
        this.xmlDatabase = null;
        this.entityName = null;
        this.iterator = new SimpleIterator<XMLItem>() {

            private Item<?> next;

            public boolean hasNext() {
                findNext();
                return this.next != null;
            }

            @Override
            protected XMLItem getNext() {
                XMLItem xmlItem = new XMLItem(this.next);
                this.next = null;
                return xmlItem;
            }

            private void findNext() {
                while (this.next == null && iterator.hasNext()) {
                    Item<?> item = (Item)iterator.next();
                    if (item.isSidekarAttached())
                        this.next = item;
                }
            }
        };
    }

    // Used during marshalling
    public Iterator<XMLItem> iterator() {

        // Get custom iterator, if any
        if (this.iterator != null)
            return this.iterator;

        // Get specified entity, or all entities at the tops of their hierarchies sorted by name
        TreeMap<String, Entity> topEntities = new TreeMap<String, Entity>();
        if (this.entityName != null)
            topEntities.put(this.entityName, this.xmlDatabase.getDatabase().getEntityMap().get(this.entityName));
        else {
            for (Entity<?> entity : this.xmlDatabase.getDatabase().getEntities()) {
                if (entity.isTopEntity())
                    topEntities.put(entity.getName(), entity);
            }
        }

        // Build an iterator that concatenates their individual iterators
        ArrayList<ItemIterator<?>> list = new ArrayList<ItemIterator<?>>(topEntities.size());
        for (Entity<?> entity : topEntities.values())
            list.add(wrapEntityIterator(entity));
        return new MultiIterator<XMLItem>(list);
    }

    private <T> ItemIterator<T> wrapEntityIterator(Entity<T> entity) {
        return new ItemIterator<T>(entity.iterator());
    }

    // Used during unmarshalling
    public void add(XMLItem xmlItem) {
        // do nothing
    }

    public XMLDatabase getXMLDatabase() {
        return this.xmlDatabase;
    }

    // Used during unmarshalling
    public static XMLData newInstance(Object obj) {
        return new XMLData((XMLDatabase)obj);
    }

    // Iterates over all items in the database, wrapped in XMLItems
    static class ItemIterator<I> extends SimpleDatabaseIterator<XMLItem, I> {

        public ItemIterator(Iterator<I> i) {
            super(i);
        }

        @Override
        protected XMLItem getNext() {
            return new XMLItem((Item)this.i.next());
        }
    }
}

